<!DOCTYPE html>
<html lang="en-us" dir="ltr" itemscope itemtype="http://schema.org/Article" data-r-output-format="html">
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="height=device-height, width=device-width, initial-scale=1.0, minimum-scale=1.0">
    <meta name="generator" content="Hugo 0.147.3">
    <meta name="generator" content="Relearn 8.2.0+220806ce3ced8264fbdd693f77f28216a8a1b6ae">
    <meta name="description" content="Tailscale’s website reads:
A secure network that just works
Zero config VPN. Installs on any device in minutes, manages firewall rules for you, and works from anywhere.
gokrazy-based devices are no exception! This guide shows you how to use Tailscale with gokrazy.
Tailscale’s networking will come in handy when accessing your gokrazy server remotely (no static DHCP leases, port-forwarding and DynDNS required!), or even to secure your communication when gokrazy is connected to an unencrypted WiFi network.">
    <meta name="author" content="">
    <meta name="twitter:card" content="summary">
    <meta name="twitter:title" content="Tailscale VPN :: gokrazy">
    <meta name="twitter:description" content="Tailscale’s website reads:
A secure network that just works
Zero config VPN. Installs on any device in minutes, manages firewall rules for you, and works from anywhere.
gokrazy-based devices are no exception! This guide shows you how to use Tailscale with gokrazy.
Tailscale’s networking will come in handy when accessing your gokrazy server remotely (no static DHCP leases, port-forwarding and DynDNS required!), or even to secure your communication when gokrazy is connected to an unencrypted WiFi network.">
    <meta property="og:url" content="https://gokrazy.org/packages/tailscale/index.html">
    <meta property="og:site_name" content="gokrazy">
    <meta property="og:title" content="Tailscale VPN :: gokrazy">
    <meta property="og:description" content="Tailscale’s website reads:
A secure network that just works
Zero config VPN. Installs on any device in minutes, manages firewall rules for you, and works from anywhere.
gokrazy-based devices are no exception! This guide shows you how to use Tailscale with gokrazy.
Tailscale’s networking will come in handy when accessing your gokrazy server remotely (no static DHCP leases, port-forwarding and DynDNS required!), or even to secure your communication when gokrazy is connected to an unencrypted WiFi network.">
    <meta property="og:locale" content="en_us">
    <meta property="og:type" content="article">
    <meta property="article:section" content="Available packages">
    <meta itemprop="name" content="Tailscale VPN :: gokrazy">
    <meta itemprop="description" content="Tailscale’s website reads:
A secure network that just works
Zero config VPN. Installs on any device in minutes, manages firewall rules for you, and works from anywhere.
gokrazy-based devices are no exception! This guide shows you how to use Tailscale with gokrazy.
Tailscale’s networking will come in handy when accessing your gokrazy server remotely (no static DHCP leases, port-forwarding and DynDNS required!), or even to secure your communication when gokrazy is connected to an unencrypted WiFi network.">
    <meta itemprop="wordCount" content="772">
    <title>Tailscale VPN :: gokrazy</title>
    <link href="/images/favicon.png?1763621646" rel="icon" type="image/png">
    <link href="/css/auto-complete/auto-complete.min.css?1763621646" rel="stylesheet">
    <script src="/js/auto-complete/auto-complete.min.js?1763621646" defer></script>
    <script src="/js/search-lunr.min.js?1763621646" defer></script>
    <script src="/js/search.min.js?1763621646" defer></script>
    <script>
      window.relearn = window.relearn || {};
      window.relearn.index_js_url="/searchindex.en.js?1763621646";
    </script>
    <script src="/js/lunr/lunr.min.js?1763621646" defer></script>
    <script src="/js/lunr/lunr.stemmer.support.min.js?1763621646" defer></script>
    <script src="/js/lunr/lunr.multi.min.js?1763621646" defer></script>
    <script src="/js/lunr/lunr.en.min.js?1763621646" defer></script>
    <script>
      window.relearn = window.relearn || {};
      window.relearn.contentLangs=['en'];
    </script>
    <link href="/fonts/fontawesome/css/fontawesome-all.min.css?1763621646" rel="stylesheet" media="print" onload="this.media='all';this.onload=null;"><noscript><link href="/fonts/fontawesome/css/fontawesome-all.min.css?1763621646" rel="stylesheet"></noscript>
    <link href="/css/perfect-scrollbar/perfect-scrollbar.min.css?1763621646" rel="stylesheet">
    <link href="/css/theme.min.css?1763621646" rel="stylesheet">
    <link href="/css/format-html.min.css?1763621646" rel="stylesheet" id="R-format-style">
    <script>
      window.relearn = window.relearn || {};
      // configuration
      window.relearn.min = `.min`;
      window.relearn.path='\/packages\/tailscale\/index.html';
      window.relearn.relBasePath='..\/..';
      window.relearn.relBaseUri='..\/..';
      window.relearn.absBaseUri='https:\/\/gokrazy.org';
      window.relearn.disableInlineCopyToClipboard=true;
      window.relearn.enableBlockCodeWrap=true;
      // legal
      window.relearn.getItem = (s,n) => {return s.getItem(n)};
      window.relearn.setItem = (s,n,v) => {return s.setItem(n,v)};
      window.relearn.removeItem = (s,n) => {return s.removeItem(n)};
      // translations
      window.T_Copy_to_clipboard = `Copy text to clipboard`;
      window.T_Copied_to_clipboard = `Text copied to clipboard!`;
      window.T_Link_copied_to_clipboard = `Link copied to clipboard!`;
      window.T_Reset_view = `Reset view`;
      window.T_View_reset = `View reset!`;
      window.T_No_results_found = `No results found for "{0}"`;
      window.T_N_results_found = `{1} results found for "{0}"`;
      window.T_Browser_unsupported_feature = `This browser doesn't support this feature`;
      // variant stuff
      window.relearn.themevariants = [ 'auto' ];
      window.relearn.customvariantname = "my-custom-variant";
      window.relearn.changeVariant = function(variant) {
        var oldVariant = document.documentElement.dataset.rThemeVariant;
        window.relearn.setItem(window.localStorage, window.relearn.absBaseUri + "/variant", variant);
        document.documentElement.dataset.rThemeVariant = variant;
        if (oldVariant != variant) {
          document.dispatchEvent( new CustomEvent('themeVariantLoaded', { detail: { variant, oldVariant } }) );
          window.relearn.markVariant();
        }
      }
      window.relearn.markVariant = function() {
        var variant = window.relearn.getItem(window.localStorage, window.relearn.absBaseUri + "/variant");
        document.querySelectorAll(".R-variantswitcher select").forEach((select) => {select.value = variant;});
      }
      window.relearn.initVariant = function() {
        var variant = window.relearn.getItem(window.localStorage, window.relearn.absBaseUri + "/variant") ?? "";
        if( variant == window.relearn.customvariantname ){
        }else if( !variant || !window.relearn.themevariants.includes(variant) ){
          variant = window.relearn.themevariants[0];
          window.relearn.setItem(window.localStorage, window.relearn.absBaseUri + "/variant", variant);
        }
        document.documentElement.dataset.rThemeVariant = variant;
      }
      window.relearn.initVariant();
      window.relearn.markVariant();
    </script>
  </head>
  <body class="mobile-support html" data-url="/packages/tailscale/index.html">
    <div id="R-body" class="default-animation">
      <div id="R-body-overlay"></div>
      <nav id="R-topbar">
        <div class="topbar-wrapper">
          <div class="topbar-sidebar-divider"></div>
          <div class="topbar-area topbar-area-start" data-area="start">
            <div class="topbar-button topbar-button-sidebar" data-content-empty="disable" data-width-s="show" data-width-m="hide" data-width-l="hide">
<span class="btn cstyle button link noborder notitle interactive"><button onclick="toggleNav()" type="button" title="Menu (CTRL&#43;ALT&#43;n)"><i class="fa-fw fas fa-bars"></i></button></span>
            </div>
            <div class="topbar-button topbar-button-toc" data-content-empty="hide" data-width-s="show" data-width-m="show" data-width-l="show">
<span class="btn cstyle button link noborder notitle interactive"><button onclick="toggleTopbarFlyout(this)" type="button" title="Table of Contents (CTRL&#43;ALT&#43;t)"><i class="fa-fw fas fa-list-alt"></i></button></span>
              <div class="topbar-content">
                <div class="topbar-content-wrapper">
<nav class="TableOfContents">
  <ul>
    <li><a href="#requirements">Requirements</a></li>
    <li><a href="#step-1-include-the-tailscale-packages">Step 1. include the tailscale packages</a></li>
    <li><a href="#step-2-set-command-line-flags">Step 2. set command-line flags</a></li>
    <li><a href="#step-3-authenticate-interactive-only">Step 3. authenticate (interactive only)</a></li>
    <li><a href="#step-4-disable-key-expiry-optional">Step 4. disable key expiry (optional)</a></li>
    <li><a href="#optional-using-gokrazy-as-a-tailscale-subnet-router">Optional: Using gokrazy as a Tailscale subnet router</a></li>
    <li><a href="#optional-accessing-gokrazy-using-tailscale-ssh">Optional: Accessing gokrazy using Tailscale SSH</a></li>
    <li><a href="#optional-tailscale-network-for-other-programs">Optional: Tailscale network for other programs</a></li>
    <li><a href="#optional-tailscale-go-listener">Optional: Tailscale Go listener</a></li>
  </ul>
</nav>
                </div>
              </div>
            </div>
          </div>
          <ol class="topbar-breadcrumbs breadcrumbs highlightable" itemscope itemtype="http://schema.org/BreadcrumbList"><li itemscope itemtype="https://schema.org/ListItem" itemprop="itemListElement" class=""><a itemprop="item" href="/index.html"><span itemprop="name">gokrazy Go appliances</span></a><meta itemprop="position" content="1">&nbsp;>&nbsp;</li><li itemscope itemtype="https://schema.org/ListItem" itemprop="itemListElement" class=""><a itemprop="item" href="/packages/index.html"><span itemprop="name">Available packages</span></a><meta itemprop="position" content="2">&nbsp;>&nbsp;</li><li itemscope itemtype="https://schema.org/ListItem" itemprop="itemListElement" class=""><span itemprop="name">Tailscale VPN</span><meta itemprop="position" content="3"></li>
          </ol>
          <div class="topbar-area topbar-area-end" data-area="end">
            <div class="topbar-button topbar-button-edit" data-content-empty="disable" data-width-s="area-more" data-width-m="show" data-width-l="show">
<span class="btn cstyle button link noborder notitle interactive"><a href="https://github.com/gokrazy/gokrazy/tree/master/website/content/packages/tailscale.md" rel="external" title="Edit (CTRL+ALT+w)"><i class="fa-fw fas fa-pen"></i></a></span>
            </div>
            <div class="topbar-button topbar-button-more" data-content-empty="hide" data-width-s="show" data-width-m="show" data-width-l="show">
<span class="btn cstyle button link noborder notitle interactive"><button onclick="toggleTopbarFlyout(this)" type="button" title="More"><i class="fa-fw fas fa-ellipsis-v"></i></button></span>
              <div class="topbar-content">
                <div class="topbar-content-wrapper">
                  <div class="topbar-area topbar-area-more" data-area="more">
                  </div>
                </div>
              </div>
            </div>
          </div>
        </div>
      </nav>
      <div id="R-main-overlay"></div>
      <main id="R-body-inner" class="highlightable packages" tabindex="-1">
        <div class="flex-block-wrapper">
<article class="default">
  <header class="headline">
  </header>

<h1 id="tailscale-vpn">Tailscale VPN</h1>

<p><a href="https://tailscale.com/" rel="external">Tailscale</a>’s website reads:</p>
<blockquote>
<p>A secure network that just works</p>
<p>Zero config VPN. Installs on any device in minutes, manages firewall rules for you, and works from anywhere.</p></blockquote>
<p>gokrazy-based devices are no exception! This guide shows you how to use
Tailscale with gokrazy.</p>
<p>Tailscale’s networking will come in handy when accessing your gokrazy server
remotely (no static DHCP leases, port-forwarding and DynDNS required!), or even
to secure your communication when gokrazy is <a href="/userguide/unencrypted-wifi/">connected to an unencrypted WiFi
network</a>.</p>
<h2 id="requirements">Requirements<span class="btn cstyle anchor copyanchor scrollanchor link noborder notitle interactive"><button type="button" title="Copy link to clipboard"><i class="fa-fw fas fa-link fa-lg"></i></button></span></h2>
<ul>
<li>Package <code>tailscale.com</code> v1.56.1 or later (latest version used automatically unless you have the package already in go.mod)</li>
<li>Volume <code>/perm/</code> needs to be initialized (instructions use <code>github.com/gokrazy/mkfs</code> to initialize)
to persist authentication over reboots.</li>
</ul>
<h2 id="step-1-include-the-tailscale-packages">Step 1. include the tailscale packages<span class="btn cstyle anchor copyanchor scrollanchor link noborder notitle interactive"><button type="button" title="Copy link to clipboard"><i class="fa-fw fas fa-link fa-lg"></i></button></span></h2>
<p>Add the Tailscale daemon <code>tailscaled</code> and CLI <code>tailscale</code> Go packages to your
gokrazy instance:</p>
<div class="highlight actionbar-wrapper wrap-code" dir="auto"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>gok add tailscale.com/cmd/tailscaled
</span></span><span style="display:flex;"><span>gok add tailscale.com/cmd/tailscale
</span></span><span style="display:flex;"><span><span style="color:#75715e"># Automatically initialize a file system on the /perm partition on first boot:</span>
</span></span><span style="display:flex;"><span>gok add github.com/gokrazy/mkfs</span></span></code></pre></div>
<h2 id="step-2-set-command-line-flags">Step 2. set command-line flags<span class="btn cstyle anchor copyanchor scrollanchor link noborder notitle interactive"><button type="button" title="Copy link to clipboard"><i class="fa-fw fas fa-link fa-lg"></i></button></span></h2>
<p>Then, open your instance’s <code>config.json</code> in your editor:</p>
<div class="highlight actionbar-wrapper wrap-code" dir="auto"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>gok edit</span></span></code></pre></div>
<p>And configure <a href="/userguide/package-config/index.html#flags">Package config: Command-line
flags</a> for Option A or Option B:</p>
<p><strong>Option A: interactive authentication</strong></p>
<div class="highlight actionbar-wrapper wrap-code" dir="auto"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;display:grid;"><code class="language-json" data-lang="json"><span style="display:flex;"><span>{
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">&#34;Hostname&#34;</span>: <span style="color:#e6db74">&#34;ts&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">&#34;Packages&#34;</span>: [
</span></span><span style="display:flex;"><span>        <span style="color:#e6db74">&#34;github.com/gokrazy/fbstatus&#34;</span>,
</span></span><span style="display:flex;"><span>        <span style="color:#e6db74">&#34;github.com/gokrazy/hello&#34;</span>,
</span></span><span style="display:flex;"><span>        <span style="color:#e6db74">&#34;github.com/gokrazy/serial-busybox&#34;</span>,
</span></span><span style="display:flex;"><span>        <span style="color:#e6db74">&#34;github.com/gokrazy/breakglass&#34;</span>,
</span></span><span style="display:flex;"><span>        <span style="color:#e6db74">&#34;tailscale.com/cmd/tailscaled&#34;</span>,
</span></span><span style="display:flex;"><span>        <span style="color:#e6db74">&#34;tailscale.com/cmd/tailscale&#34;</span>,
</span></span><span style="display:flex;"><span>        <span style="color:#e6db74">&#34;github.com/gokrazy/mkfs&#34;</span>
</span></span><span style="display:flex;"><span>    ],
</span></span><span style="display:flex; background-color:#3c3d38"><span>    <span style="color:#f92672">&#34;PackageConfig&#34;</span>: {
</span></span><span style="display:flex; background-color:#3c3d38"><span>        <span style="color:#f92672">&#34;tailscale.com/cmd/tailscale&#34;</span>: {
</span></span><span style="display:flex; background-color:#3c3d38"><span>            <span style="color:#f92672">&#34;CommandLineFlags&#34;</span>: [
</span></span><span style="display:flex; background-color:#3c3d38"><span>                <span style="color:#e6db74">&#34;up&#34;</span>
</span></span><span style="display:flex; background-color:#3c3d38"><span>            ]
</span></span><span style="display:flex; background-color:#3c3d38"><span>        }
</span></span><span style="display:flex; background-color:#3c3d38"><span>    }
</span></span><span style="display:flex;"><span>}</span></span></code></pre></div>

<p><strong>Option B: unattended authentication with auth key</strong></p>
<p>Alternatively,
navigate to <a href="https://login.tailscale.com/" rel="external" title="Tailscale management console login.tailscale.com">Tailscale console</a> and open Settings / Keys. Generate auth key.</p>
<p>Include the key to tailscale flags:</p>
<div class="highlight actionbar-wrapper wrap-code" dir="auto"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;display:grid;"><code class="language-json" data-lang="json"><span style="display:flex;"><span>{
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">&#34;Hostname&#34;</span>: <span style="color:#e6db74">&#34;ts&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">&#34;Packages&#34;</span>: [
</span></span><span style="display:flex;"><span>        <span style="color:#e6db74">&#34;github.com/gokrazy/fbstatus&#34;</span>,
</span></span><span style="display:flex;"><span>        <span style="color:#e6db74">&#34;github.com/gokrazy/hello&#34;</span>,
</span></span><span style="display:flex;"><span>        <span style="color:#e6db74">&#34;github.com/gokrazy/serial-busybox&#34;</span>,
</span></span><span style="display:flex;"><span>        <span style="color:#e6db74">&#34;github.com/gokrazy/breakglass&#34;</span>,
</span></span><span style="display:flex;"><span>        <span style="color:#e6db74">&#34;tailscale.com/cmd/tailscaled&#34;</span>,
</span></span><span style="display:flex;"><span>        <span style="color:#e6db74">&#34;tailscale.com/cmd/tailscale&#34;</span>,
</span></span><span style="display:flex;"><span>        <span style="color:#e6db74">&#34;github.com/gokrazy/mkfs&#34;</span>
</span></span><span style="display:flex;"><span>    ],
</span></span><span style="display:flex; background-color:#3c3d38"><span>    <span style="color:#f92672">&#34;PackageConfig&#34;</span>: {
</span></span><span style="display:flex; background-color:#3c3d38"><span>        <span style="color:#f92672">&#34;tailscale.com/cmd/tailscale&#34;</span>: {
</span></span><span style="display:flex; background-color:#3c3d38"><span>            <span style="color:#f92672">&#34;CommandLineFlags&#34;</span>: [
</span></span><span style="display:flex; background-color:#3c3d38"><span>                <span style="color:#e6db74">&#34;up&#34;</span>,
</span></span><span style="display:flex; background-color:#3c3d38"><span>                <span style="color:#e6db74">&#34;--auth-key=tskey-AAAAAAAAAAAA-AAAAAAAAAAAAAAAAAAAAAA&#34;</span>
</span></span><span style="display:flex; background-color:#3c3d38"><span>            ]
</span></span><span style="display:flex; background-color:#3c3d38"><span>        }
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>}</span></span></code></pre></div>

<p>Then, deploy as usual:</p>
<div class="highlight actionbar-wrapper wrap-code" dir="auto"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>gok update</span></span></code></pre></div>
<h2 id="step-3-authenticate-interactive-only">Step 3. authenticate (interactive only)<span class="btn cstyle anchor copyanchor scrollanchor link noborder notitle interactive"><button type="button" title="Copy link to clipboard"><i class="fa-fw fas fa-link fa-lg"></i></button></span></h2>
<p>Skip this step if you are using option B with auth key.</p>
<ol>
<li>Navigate to your gokrazy web interface with browser using the URL displayed
by the <code>gok</code> CLI.</li>
<li>Open the service <code>/user/tailscale</code> and find the login URL.</li>
<li>Open the link with browser and log in to Tailscale and authorize the client.</li>
</ol>
<h2 id="step-4-disable-key-expiry-optional">Step 4. disable key expiry (optional)<span class="btn cstyle anchor copyanchor scrollanchor link noborder notitle interactive"><button type="button" title="Copy link to clipboard"><i class="fa-fw fas fa-link fa-lg"></i></button></span></h2>
<p>You are now connected to Tailscale and you can access your gokrazy instance
over Tailscale.</p>

<details open class="box cstyle notices note">
  <summary class="box-label" tabindex="-1">
    <i class="fa-fw fas fa-exclamation-circle"></i> 
    Note
  </summary>
  <div class="box-content">
<p>Tailscale requires re-authentication periodically.
You can disable key expiry from <a href="https://login.tailscale.com/" rel="external" title="Tailscale management console login.tailscale.com">Tailscale console</a> for the gokrazy
instance to not require login every 3 months.</p>
  </div>
</details>
<h2 id="optional-using-gokrazy-as-a-tailscale-subnet-router">Optional: Using gokrazy as a Tailscale subnet router<span class="btn cstyle anchor copyanchor scrollanchor link noborder notitle interactive"><button type="button" title="Copy link to clipboard"><i class="fa-fw fas fa-link fa-lg"></i></button></span></h2>
<p>To allow hosts in your Tailnet to access devices in your network other
than your gokrazy appliance, you can set up Tailscale as a subnet router.</p>
<div class="highlight actionbar-wrapper wrap-code" dir="auto"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;display:grid;"><code class="language-json" data-lang="json"><span style="display:flex;"><span>{
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">&#34;Hostname&#34;</span>: <span style="color:#e6db74">&#34;ts&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">&#34;Packages&#34;</span>: [
</span></span><span style="display:flex;"><span>        <span style="color:#e6db74">&#34;github.com/gokrazy/serial-busybox&#34;</span>,
</span></span><span style="display:flex;"><span>        <span style="color:#e6db74">&#34;tailscale.com/cmd/tailscaled&#34;</span>,
</span></span><span style="display:flex;"><span>        <span style="color:#e6db74">&#34;tailscale.com/cmd/tailscale&#34;</span>
</span></span><span style="display:flex;"><span>    ],
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">&#34;PackageConfig&#34;</span>: {
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">&#34;tailscale.com/cmd/tailscale&#34;</span>: {
</span></span><span style="display:flex;"><span>            <span style="color:#f92672">&#34;CommandLineFlags&#34;</span>: [
</span></span><span style="display:flex;"><span>                <span style="color:#e6db74">&#34;up&#34;</span>,
</span></span><span style="display:flex; background-color:#3c3d38"><span>                <span style="color:#e6db74">&#34;--advertise-routes=192.168.0.0/24&#34;</span>
</span></span><span style="display:flex;"><span>            ]
</span></span><span style="display:flex;"><span>        }
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>}</span></span></code></pre></div>

<p>Starting <a href="https://github.com/tailscale/tailscale/issues/11405" rel="external">Tailscale version v1.64</a>,
IP forwarding is automatically enabled on Gokrazy.</p>
<h2 id="optional-accessing-gokrazy-using-tailscale-ssh">Optional: Accessing gokrazy using Tailscale SSH<span class="btn cstyle anchor copyanchor scrollanchor link noborder notitle interactive"><button type="button" title="Copy link to clipboard"><i class="fa-fw fas fa-link fa-lg"></i></button></span></h2>
<p>To access the gokrazy appliance using SSH authenticating using Tailscale,
you can enable Tailscale SSH.</p>
<div class="highlight actionbar-wrapper wrap-code" dir="auto"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;display:grid;"><code class="language-json" data-lang="json"><span style="display:flex;"><span>{
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">&#34;Hostname&#34;</span>: <span style="color:#e6db74">&#34;ts&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">&#34;Packages&#34;</span>: [
</span></span><span style="display:flex;"><span>        <span style="color:#e6db74">&#34;github.com/gokrazy/serial-busybox&#34;</span>,
</span></span><span style="display:flex;"><span>        <span style="color:#e6db74">&#34;tailscale.com/cmd/tailscaled&#34;</span>,
</span></span><span style="display:flex;"><span>        <span style="color:#e6db74">&#34;tailscale.com/cmd/tailscale&#34;</span>
</span></span><span style="display:flex;"><span>    ],
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">&#34;PackageConfig&#34;</span>: {
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">&#34;tailscale.com/cmd/tailscale&#34;</span>: {
</span></span><span style="display:flex;"><span>            <span style="color:#f92672">&#34;CommandLineFlags&#34;</span>: [
</span></span><span style="display:flex;"><span>                <span style="color:#e6db74">&#34;up&#34;</span>,
</span></span><span style="display:flex; background-color:#3c3d38"><span>                <span style="color:#e6db74">&#34;--ssh=true&#34;</span>
</span></span><span style="display:flex;"><span>            ]
</span></span><span style="display:flex;"><span>        }
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>}</span></span></code></pre></div>

<p>Note that Tailscale SSH <a href="https://tailscale.com/kb/1193/tailscale-ssh#ensure-tailscale-ssh-is-permitted-in-acls" rel="external">needs to be allowed by your Tailnet ACL</a>.
You can configure to allow, for example, each user to access their own
devices using Tailscale SSH, or define which hosts users can access.</p>
<h2 id="optional-tailscale-network-for-other-programs">Optional: Tailscale network for other programs<span class="btn cstyle anchor copyanchor scrollanchor link noborder notitle interactive"><button type="button" title="Copy link to clipboard"><i class="fa-fw fas fa-link fa-lg"></i></button></span></h2>
<p>Before Tailscale v1.56.1, Tailscale used <a href="https://tailscale.com/kb/1112/userspace-networking/" rel="external" title="Userspace networking mode (for containers)">Userspace networking</a> mode on gokrazy,
meaning you needed to use Tailscale as an HTTP proxy to establish outgoing
connections into your tailnet.</p>
<p>With Tailscale v1.56.1 and newer, programs running on gokrazy can connect to
other devices in your tailnet without extra steps! 🎉 DNS resolution and TCP
connections work out of the box.</p>
<h2 id="optional-tailscale-go-listener">Optional: Tailscale Go listener<span class="btn cstyle anchor copyanchor scrollanchor link noborder notitle interactive"><button type="button" title="Copy link to clipboard"><i class="fa-fw fas fa-link fa-lg"></i></button></span></h2>
<p>Before Tailscale v1.56.1, Tailscale used <a href="https://tailscale.com/kb/1112/userspace-networking/" rel="external" title="Userspace networking mode (for containers)">Userspace networking</a> mode on gokrazy,
meaning you needed to use the tsnet package if you wanted to restrict a listener
to Tailscale.</p>
<p>With Tailscale v1.56.1 and newer, you can listen on Tailscale addresses and use
<a href="https://pkg.go.dev/tailscale.com/client/tailscale#LocalClient.WhoIs" rel="external">LocalClient.WhoIs</a>
to obtain the remote identity:</p>
<div class="highlight actionbar-wrapper wrap-code" dir="auto"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#f92672">package</span> <span style="color:#a6e22e">main</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">import</span> (
</span></span><span style="display:flex;"><span>	<span style="color:#e6db74">&#34;flag&#34;</span>
</span></span><span style="display:flex;"><span>	<span style="color:#e6db74">&#34;fmt&#34;</span>
</span></span><span style="display:flex;"><span>	<span style="color:#e6db74">&#34;log&#34;</span>
</span></span><span style="display:flex;"><span>	<span style="color:#e6db74">&#34;net/http&#34;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>	<span style="color:#e6db74">&#34;tailscale.com/client/tailscale&#34;</span>
</span></span><span style="display:flex;"><span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">main</span>() {
</span></span><span style="display:flex;"><span>	<span style="color:#a6e22e">listen</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">flag</span>.<span style="color:#a6e22e">String</span>(<span style="color:#e6db74">&#34;listen&#34;</span>, <span style="color:#e6db74">&#34;gokrazy.monkey-turtle.ts.net:8111&#34;</span>, <span style="color:#e6db74">&#34;[host]:port listen address&#34;</span>)
</span></span><span style="display:flex;"><span>	<span style="color:#a6e22e">allowedUser</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">flag</span>.<span style="color:#a6e22e">String</span>(<span style="color:#e6db74">&#34;allowed_user&#34;</span>, <span style="color:#e6db74">&#34;&#34;</span>, <span style="color:#e6db74">&#34;the name of a tailscale user to allow&#34;</span>)
</span></span><span style="display:flex;"><span>	<span style="color:#a6e22e">flag</span>.<span style="color:#a6e22e">Parse</span>()
</span></span><span style="display:flex;"><span>	<span style="color:#a6e22e">log</span>.<span style="color:#a6e22e">Printf</span>(<span style="color:#e6db74">&#34;starting HTTP listener on %s&#34;</span>, <span style="color:#f92672">*</span><span style="color:#a6e22e">listen</span>)
</span></span><span style="display:flex;"><span>	<span style="color:#66d9ef">var</span> <span style="color:#a6e22e">ts</span> <span style="color:#a6e22e">tailscale</span>.<span style="color:#a6e22e">LocalClient</span>
</span></span><span style="display:flex;"><span>	<span style="color:#a6e22e">httpsrv</span> <span style="color:#f92672">:=</span> <span style="color:#f92672">&amp;</span><span style="color:#a6e22e">http</span>.<span style="color:#a6e22e">Server</span>{
</span></span><span style="display:flex;"><span>		<span style="color:#a6e22e">Addr</span>: <span style="color:#f92672">*</span><span style="color:#a6e22e">listen</span>,
</span></span><span style="display:flex;"><span>		<span style="color:#a6e22e">Handler</span>: <span style="color:#a6e22e">http</span>.<span style="color:#a6e22e">HandlerFunc</span>(<span style="color:#66d9ef">func</span>(<span style="color:#a6e22e">w</span> <span style="color:#a6e22e">http</span>.<span style="color:#a6e22e">ResponseWriter</span>, <span style="color:#a6e22e">r</span> <span style="color:#f92672">*</span><span style="color:#a6e22e">http</span>.<span style="color:#a6e22e">Request</span>) {
</span></span><span style="display:flex;"><span>			<span style="color:#a6e22e">who</span>, <span style="color:#a6e22e">err</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">ts</span>.<span style="color:#a6e22e">WhoIs</span>(<span style="color:#a6e22e">r</span>.<span style="color:#a6e22e">Context</span>(), <span style="color:#a6e22e">r</span>.<span style="color:#a6e22e">RemoteAddr</span>)
</span></span><span style="display:flex;"><span>			<span style="color:#66d9ef">if</span> <span style="color:#a6e22e">err</span> <span style="color:#f92672">!=</span> <span style="color:#66d9ef">nil</span> {
</span></span><span style="display:flex;"><span>				<span style="color:#a6e22e">http</span>.<span style="color:#a6e22e">Error</span>(<span style="color:#a6e22e">w</span>, <span style="color:#a6e22e">err</span>.<span style="color:#a6e22e">Error</span>(), <span style="color:#a6e22e">http</span>.<span style="color:#a6e22e">StatusInternalServerError</span>)
</span></span><span style="display:flex;"><span>				<span style="color:#66d9ef">return</span>
</span></span><span style="display:flex;"><span>			}
</span></span><span style="display:flex;"><span>			<span style="color:#66d9ef">if</span> <span style="color:#a6e22e">who</span>.<span style="color:#a6e22e">UserProfile</span>.<span style="color:#a6e22e">LoginName</span> <span style="color:#f92672">!=</span> <span style="color:#f92672">*</span><span style="color:#a6e22e">allowedUser</span> <span style="color:#f92672">||</span> <span style="color:#f92672">*</span><span style="color:#a6e22e">allowedUser</span> <span style="color:#f92672">==</span> <span style="color:#e6db74">&#34;&#34;</span> {
</span></span><span style="display:flex;"><span>				<span style="color:#a6e22e">err</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">fmt</span>.<span style="color:#a6e22e">Sprintf</span>(<span style="color:#e6db74">&#34;you are logged in as %q (userprofile: %+v), but -allowed_user flag does not match!&#34;</span>, <span style="color:#a6e22e">who</span>.<span style="color:#a6e22e">UserProfile</span>.<span style="color:#a6e22e">LoginName</span>, <span style="color:#a6e22e">who</span>.<span style="color:#a6e22e">UserProfile</span>)
</span></span><span style="display:flex;"><span>				<span style="color:#a6e22e">log</span>.<span style="color:#a6e22e">Printf</span>(<span style="color:#e6db74">&#34;forbidden: %v&#34;</span>, <span style="color:#a6e22e">err</span>)
</span></span><span style="display:flex;"><span>				<span style="color:#a6e22e">http</span>.<span style="color:#a6e22e">Error</span>(<span style="color:#a6e22e">w</span>, <span style="color:#a6e22e">err</span>, <span style="color:#a6e22e">http</span>.<span style="color:#a6e22e">StatusForbidden</span>)
</span></span><span style="display:flex;"><span>				<span style="color:#66d9ef">return</span>
</span></span><span style="display:flex;"><span>			}
</span></span><span style="display:flex;"><span>			<span style="color:#a6e22e">fmt</span>.<span style="color:#a6e22e">Fprintf</span>(<span style="color:#a6e22e">w</span>, <span style="color:#e6db74">&#34;hey there, %q! this message is served via tailscale from gokrazy!&#34;</span>, <span style="color:#a6e22e">who</span>.<span style="color:#a6e22e">UserProfile</span>.<span style="color:#a6e22e">LoginName</span>)
</span></span><span style="display:flex;"><span>		}),
</span></span><span style="display:flex;"><span>	}
</span></span><span style="display:flex;"><span>	<span style="color:#a6e22e">log</span>.<span style="color:#a6e22e">Fatal</span>(<span style="color:#a6e22e">httpsrv</span>.<span style="color:#a6e22e">ListenAndServe</span>())
</span></span><span style="display:flex;"><span>}</span></span></code></pre></div>
<ol>
<li>Deploy this program to your gokrazy device</li>
<li>Open the listening address in your browser, e.g. <a href="http://gokrazy.monkey-turtle.ts.net:8111" rel="external">http://gokrazy.monkey-turtle.ts.net:8111</a></li>
<li>Specify the <code>--allowed_user</code> flag to verify that tailscale authentication works as expected</li>
</ol>

  <footer class="footline">
  </footer>
</article>
        </div>
      </main>
    </div>
    <aside id="R-sidebar" class="default-animation">
      <div id="R-header-topbar" class="default-animation"></div>
      <div id="R-header-wrapper" class="default-animation">
        <div id="R-header" class="default-animation">
<img src="/logo.svg" width="100%" alt="gokrazy logo" title="gokrazy logo">

        </div>
        <search><form action="/search/index.html" method="get">
          <div class="searchbox default-animation">
            <button class="search-detail" type="submit" title="Search (CTRL+ALT+f)"><i class="fas fa-search"></i></button>
            <label class="a11y-only" for="R-search-by">Search</label>
            <input data-search-input id="R-search-by" name="search-by" class="search-by" type="search" placeholder="Search...">
            <button class="search-clear" type="button" data-search-clear="" title="Clear search"><i class="fas fa-times" title="Clear search"></i></button>
          </div>
        </form></search>
      </div>
      <div id="R-homelinks" class="default-animation homelinks">
        <div class="R-menu-divider default-animation">
          <hr class="padding">
        </div>
        <div class="R-sidebarmenu R-shortcutmenu-homelinks">
          <ul class="space collapsible-menu">
            <li class="" data-nav-url="/index.html"><a class="padding" href="/index.html"><i class="fa-fw fas fa-home"></i> Home</a></li>
          </ul>
        </div>
        <div class="R-menu-divider default-animation">
          <hr class="padding">
        </div>
        <div class="R-sidebarmenu R-shortcutmenu-headercontrols">
          <ul class="">
          </ul>
        </div>
        <div class="R-menu-divider default-animation">
          <hr class="padding">
        </div>
      </div>
      <div id="R-content-wrapper" class="highlightable">
        <div class="R-sidebarmenu R-shortcutmenu-main">
          <ul class="enlarge morespace collapsible-menu">
            <li class="" data-nav-url="/quickstart/index.html"><a class="padding" href="/quickstart/index.html"><b>1. </b>Quickstart</a></li>
            <li class="" data-nav-url="/platforms/index.html"><a class="padding" href="/platforms/index.html"><b>2. </b>Supported platforms</a></li>
            <li class="" data-nav-url="/userguide/index.html"><a class="padding" href="/userguide/index.html"><b>3. </b>Configuration</a><ul id="R-subsections-74b4df3a3d9365853f09e9f1dfe7bbba" class="collapsible-menu"></ul></li>
            <li class="parent " data-nav-url="/packages/index.html"><a class="padding" href="/packages/index.html"><b>4. </b>Available packages</a><ul id="R-subsections-bdf1e8ce92aa8ff6883c617f10480430" class="collapsible-menu">
            <li class="" data-nav-url="/packages/showcase/index.html"><a class="padding" href="/packages/showcase/index.html">Showcase</a></li>
            <li class="active " data-nav-url="/packages/tailscale/index.html"><a class="padding" href="/packages/tailscale/index.html">Tailscale VPN</a></li>
            <li class="" data-nav-url="/packages/caddy-http-server/index.html"><a class="padding" href="/packages/caddy-http-server/index.html">Caddy HTTP server</a></li>
            <li class="" data-nav-url="/packages/minio/index.html"><a class="padding" href="/packages/minio/index.html">MinIO object storage</a></li>
            <li class="" data-nav-url="/packages/docker-containers/index.html"><a class="padding" href="/packages/docker-containers/index.html">Docker containers</a></li>
            <li class="" data-nav-url="/packages/prometheus/index.html"><a class="padding" href="/packages/prometheus/index.html">Prometheus</a></li></ul></li>
            <li class="" data-nav-url="/development/index.html"><a class="padding" href="/development/index.html"><b>5. </b>Package development</a><ul id="R-subsections-54b0ce3c97b111c8cd4d107325fd580c" class="collapsible-menu"></ul></li>
          </ul>
        </div>
        <div class="R-sidebarmenu R-shortcutmenu-shortcuts">
          <div class="nav-title padding">More</div>
          <ul class="space collapsible-menu">
            <li class="" data-nav-url="https://github.com/gokrazy/gokrazy"><a class="padding" href="https://github.com/gokrazy/gokrazy" rel="external"><i class='fab fa-github'></i> GitHub repo</a></li>
          </ul>
        </div>
        <div id="R-footer-margin"></div>
        <div class="R-menu-divider default-animation">
          <hr class="padding">
        </div>
        <div class="R-sidebarmenu R-shortcutmenu-footercontrols">
          <ul class="">
          </ul>
        </div>
<div id="R-footer"><p>Built with <a href="https://github.com/McShelby/hugo-theme-relearn" title="love"><i class="fas fa-heart"></i></a> by <a href="https://gohugo.io/">Hugo</a></p></div>
      </div>
    </aside>
    <script src="/js/perfect-scrollbar/perfect-scrollbar.min.js?1763621646" defer></script>
    <script src="/js/theme.min.js?1763621646" defer></script>
    <div id="toast-container" role="status" aria-live="polite" aria-atomic="false"></div>
  </body>
</html>
